/*
 * @Author: huangwei
 * @Date: 2021-05-17 14:49:10
 * @LastEditors: 黄威
 * @LastEditTime: 2022-02-15 18:04:37
 * @Description: 地图操作相关
 */
import { Component, Vue } from 'vue-property-decorator'

import L from 'leaflet'
import { toCentoid, toBoundary } from '@/utils/view'

@Component
export default class ViewMixin extends Vue {
    map!: L.Map

    /**
     * @public
     * 地图容器变化调用的接口
     */
    resize() {
        this.map.invalidateSize(true)
    }

    /**
     * @public
     * 交互是否激活（即鼠标是否可以拖动地图）
     * @param active
     */
    setInteractiveActive(active: boolean = true) {
        this.map.dragging[active ? 'enable' : 'disable']()
    }

    /**
     * @public
     * 获取当前缩放层级
     * @returns {Number}
     */
    getZoom(): number {
        return this.map.getZoom()
    }

    /**
     * @public
     * 获取当前视图范围
     * @returns {Number[]}
     */
    getBounds(): number[] {
        const bounds = this.map.getBounds()
        return [
            bounds.getWest(),
            bounds.getSouth(),
            bounds.getEast(),
            bounds.getNorth(),
        ]
    }

    /**
     * @public
     * 放大
     */
    zoomIn() {
        this.map.zoomIn()
    }

    /**
     * @public
     * 缩小
     */
    zoomOut() {
        this.map.zoomOut()
    }

    /**
     * @public
     * 定位到范围 [minx, miny, maxx, maxy]
     * @param {Number[]} extent 范围
     * @param {any} options 定位参数
     */
    zoom2Extent(extent: any, options?: any) {
        extent = toBoundary(extent)

        this.map.fitBounds(
            [
                [extent[1], extent[0]],
                [extent[3], extent[2]],
            ],
            options
        )
    }

    /**
     * @public
     * 定位到中心点 [lng, lat]
     * @param {Number[]} centoid 中心点
     * @param {any} options 定位参数
     */
    zoom2Centoid(centoid: any, options?: any) {
        centoid = toCentoid(centoid)

        const zoom =
            options && options.zoom >= 0 ? options.zoom : this.getZoom()
        // 动画
        const opts = options || {}
        if (!options || !options.duration || options.duration <= 0) {
            delete opts.duration
            opts.animate = false
        }

        this.map.flyTo([centoid[1], centoid[0]], zoom, opts)
    }

    /**
     * @public
     * 定位到几何
     * @param {any} geom 请查看 `L.Layer` `Cesium.Entity`
     * @param {any} options 定位参数
     */
    zoom2Geometry(geom: any, options?: any) {
        // 转为 leaflet 对象
        if (!(geom instanceof L.Layer)) {
            geom = L.geoJSON(geom)
        }

        // L.Path等
        if (geom.getBounds) {
            this.map.fitBounds(geom.getBounds(), options)
        }
        // marker数据（点类型）
        else if (geom.getLatLng) {
            this.map.setView(geom.getLatLng(), (options ?? {}).maxZoom, options)
        }
    }

    /**
     * @public
     * 像素点转地图坐标
     * @param {{x:Number, y:Number}} pixelPos
     * @returns {Number[]}
     */
    getMapCoords4Pixel(pixelPos: any): [number, number] {
        const ll = this.map.containerPointToLatLng(pixelPos)
        return [ll.lng, ll.lat]
    }

    /**
     * @public
     * 地图坐标转像素点
     * @param {[Number, Number, Number?] | { lng: Number; lat: Number }} mapCoords
     * @returns {{x:Number, y:Number}}
     */
    getPixel4MapCoords(mapCoords: L.LatLngExpression): L.Point {
        const ll = toCentoid(mapCoords)
        return this.map.latLngToContainerPoint([ll[1], ll[0]])
    }

    /**
     * @public
     * 判断指定坐标是否在图上可见
     * @param {[Number, Number, Number?] | { lng: Number; lat: Number } | { x: Number; y: Number }} point
     * @returns {Boolean}
     */
    containPoint(point: any): boolean {
        const [x, y] = toCentoid(point)
        return this.map.getBounds().contains([y, x])
    }
}
